home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / PacMan / Player.m < prev    next >
Encoding:
Text File  |  1992-06-29  |  6.1 KB  |  212 lines

  1.  
  2. /* Generated by Interface Builder */
  3.  
  4. // Handles moving and rendering the Pac, whether under player or demo control.
  5.  
  6. #import <libc.h>
  7. #import "Player.h"
  8. #import "Monster.h"
  9. #import "Maze.h"
  10. #import "PacManView.h"
  11. #import "PlayerUpView.h"
  12. #import <appkit/NXImage.h>
  13. #import <appkit/Application.h>
  14. #import <appkit/graphics.h>
  15. #import "PacMovement.h"
  16.  
  17. // used to translate PAC_<direction> to px, py velocities
  18. static int pacxvec[9] = { 0, 2, -2, 0,  0, 0, 0, 0,  0 };
  19. static int pacyvec[9] = { 0, 0,  0, 0, -2, 0, 0, 0,  2 };
  20. //static int   opdir[9] = { 0, 2,  1, 0,  8, 0, 0, 0,  4 };
  21. static int bothdir[9] = { 0, 3,  3, 0, 12, 0, 0, 0, 12 };
  22.  
  23. // the y-coord in the Pacs.tiff image indexed by direction
  24. static int pacypic[9] = { 0, 5 * PAC_WIDTH,  4 * PAC_WIDTH, 0,
  25.     6 * PAC_WIDTH, 0, 0, 0, 7 * PAC_WIDTH };
  26. static int pacydie[9] = { 0, 1 * PAC_WIDTH,  0,              0,
  27.     2 * PAC_WIDTH, 0, 0, 0, 3 * PAC_WIDTH };
  28.  
  29.  
  30. @implementation Player
  31.  
  32. - init                        // initialize the player
  33. {
  34.     [super init];
  35.     pacs = [NXImage findImageNamed:"Pacs.tiff"];
  36.     nextDir = PAC_STOP;
  37.     curDir = PAC_STOP;
  38.     return self;
  39. }
  40.  
  41. - (BOOL)newPlayer            // get and set up a new Pac.  Returns NO if can't
  42. {
  43.     if (![pacsLeft nextPac]) return NO;        // no pacs left
  44.     [self resetPlayer];
  45.     return YES;
  46. }
  47.  
  48. - resetPlayer                // reset all player info
  49. {
  50.     curDir = PAC_RIGHT;
  51.     nextDir = PAC_RIGHT;
  52.     px = 0; py = 0;
  53.     state = PAC_ALIVE;
  54.     cycle = 0;
  55.     lastx = - 4 * GHOST_SIZE;
  56.     lasty = - 4 * GHOST_SIZE;
  57.     [maze playerPosition:&myX :&myY];
  58.     return self;
  59. }
  60.  
  61. - (BOOL)pacAlive                // returns YES if Pac is alive
  62. {
  63.     if (state < PAC_ALIVE) return NO;
  64.     return YES;
  65. }
  66.  
  67. - demoMove:sender    // handles Pac movement during the demo sequence
  68. {
  69.     int i, zz; float xx, yy; int zi = 1;
  70.     int ghost_x = 0;
  71.     int ghost_y = 0;
  72.     int closest = (BLOCK_WIDTH + BLOCK_HEIGHT) * GHOST_SIZE;
  73.     register int tdir = 0x0f;
  74.     register int sense = 0;
  75.     int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
  76.     
  77.     // keep us in the track between maze walls
  78.     if (dx) tdir &= 0x03;
  79.     if (dy) tdir &= 0x0c;
  80.     
  81.     for (i=0; i<4; i++) {    // find closest ghost; run away from it.
  82.         [[sender ghost:i] at:&xx :&yy];
  83.         zz = abs(xx - myX) + abs(yy - myY);
  84.         if (zz < closest) {
  85.             zi = i;
  86.             closest = zz;
  87.             ghost_x = xx;
  88.             ghost_y = yy;
  89.     }    }
  90.     
  91. // first, find the directions in which pac can go
  92.     if ((abs(ghost_x - myX) >= GHOST_SIZE * 2 - 2) ||
  93.             (abs(ghost_y - myY) >= GHOST_SIZE * 2 - 2)) {
  94.         if ([maze playerWall:(myX+GHOST_SIZE) :myY] || (px < 0))
  95.             tdir &= ~0x01;
  96.         if ([maze playerWall:(myX-1) :myY] || (px > 0)) tdir &= ~0x02;
  97.         if ([maze playerWall:myX :(myY-1)] || (py > 0)) tdir &= ~0x04;
  98.         if ([maze playerWall:myX :(myY+GHOST_SIZE)] || (py < 0))
  99.             tdir &= ~0x08;
  100.         if ((random() & 0x0f) > 10) {
  101.             // worry about shorter distance first.
  102.             if (abs(ghost_x - myX) < abs(ghost_y - myY)) {
  103.                 if (tdir & 0x03) tdir &= 0x03;
  104.             } else {
  105.                 if (tdir & 0x0c) tdir &= 0x0c;
  106.         }    }
  107.     } else { // if a ghost is close by, we can reverse direction.
  108.         if ([maze playerWall:myX     :(myY-1)]) tdir &= ~0x04;
  109.         if ([maze playerWall:myX :(myY+GHOST_SIZE)]) tdir &= ~0x08;
  110.         if ([maze playerWall:(myX+GHOST_SIZE) :myY]) tdir &= ~0x01;
  111.         if ([maze playerWall:(myX-1) :myY]) tdir &= ~0x02;
  112.     }
  113.     
  114. // now choose the new direction for the pac
  115.     if ([[sender ghost:zi] canBeEaten]) {
  116.     // chase an eatable ghost
  117.         sense = demofind[sgn(ghost_y - myY) + 1][sgn(ghost_x - myX) + 1];
  118.         px = chasexvec[tdir][sense];
  119.         py = chaseyvec[tdir][sense];
  120.     } else {
  121.         if (((abs(ghost_x - myX) <= GHOST_SIZE * 2 - 2) &&
  122.                 (abs(ghost_y - myY) <= GHOST_SIZE * 2 - 2)) ||
  123.                 ((random() & 0x0f) > 2)) {    // if close or random allows, go
  124.                 // according to program
  125.             sense = demofind[sgn(ghost_y - myY) + 1][sgn(ghost_x - myX) + 1];
  126.         } else {    // random direction occasionally
  127.              sense = random() & 0x07;
  128.         }
  129.         px = demoxvec[tdir][sense];
  130.         py = demoyvec[tdir][sense];
  131.     }    // set up the direction:
  132.     curDir = demoDir[sgn(py) + 1][sgn(px) + 1];
  133.     return self;
  134. }
  135.  
  136. - move:sender                // Move the PacMan one animation frame
  137. {
  138.     int dx = myX % PAC_WIDTH; int dy = myY % PAC_WIDTH;
  139.     register int tdir = 0x0f;
  140.  
  141.     if (state < PAC_DEAD) { // Pac is dying
  142.         state++;
  143.     } else if (state >= PAC_ALIVE) { // Pac is alive, so move it
  144.  
  145.         if ([gameView demoMode]) {
  146.         // if demo mode, we move the pac ourselves
  147.             [self demoMove:sender];
  148.         } else {
  149.         // if not demo mode, we let the user decide how to move the pac.
  150.             // first, find the directions in which the pac can go
  151.             if (dx || dy) tdir = bothdir[curDir]; // curr. dir or opp.
  152.                 // direction are allowed; other dir. only at junctions.
  153.             if ([maze playerWall:(myX+PAC_WIDTH) :myY]) tdir &= ~PAC_RIGHT;
  154.             if ([maze playerWall:(myX-2) :myY]) tdir &= ~PAC_LEFT;
  155.             if ([maze playerWall:myX     :(myY-2)]) tdir &= ~PAC_DOWN;
  156.             if ([maze playerWall:myX :(myY+PAC_WIDTH)]) tdir &= ~PAC_UP;
  157.  
  158.             // see if we can go the "next" direction the player wants or not...
  159.             if (nextDir & tdir) { // we can!
  160.                 curDir = nextDir;
  161.             }
  162.             // now choose the new direction for the pac
  163.             //   the "&" makes sure we stop if we can't go further in this dir.
  164.             px = pacxvec[curDir & tdir];
  165.             py = pacyvec[curDir & tdir];
  166.         }
  167.  
  168.     } else { // Pac is dead so do nothing.
  169.         px = 0; py = 0;
  170.     }
  171.     return self;
  172. }
  173.  
  174. - newDirection:(int)newDir        // send Pac in new direction.
  175. {
  176.     nextDir = newDir;
  177.     return self;
  178. }
  179.  
  180. - pacDie { state = PAC_DYING; return self; }    // the pac will melt
  181.  
  182. - renderAt:(int)posx :(int)posy move:(BOOL)moveOk    // draw pac
  183.         // you should lock focus on view that gets the Pac first.
  184. {
  185.     NXRect from;
  186.     NXPoint pos;
  187.  
  188.     [super renderAt:posx :posy move:moveOk];
  189.     
  190.     pos.x = myX + posx; pos.y = myY + posy;
  191.  
  192.     if (state == PAC_ALIVE) {    // draw living pac
  193.         NXSetRect(&from, cycle * PAC_WIDTH, pacypic[curDir],
  194.             PAC_WIDTH, PAC_WIDTH);
  195.     } else if (state < PAC_DEAD) { // draw dying pac
  196.         NXSetRect(&from, (state - PAC_DYING) * PAC_WIDTH, pacydie[curDir],
  197.             PAC_WIDTH, PAC_WIDTH);
  198.         if (!cycle) state++;
  199.         if (cycle == 1) cycle = -1; // change period while we die
  200.     } else { // if dead, draw no pac (upper rt. of image is blank)
  201.         NXSetRect(&from, 10 * PAC_WIDTH, 7 * PAC_WIDTH,
  202.             PAC_WIDTH, PAC_WIDTH);
  203.     }
  204.     [pacs composite:NX_SOVER fromRect:&from toPoint:&pos];
  205.  
  206.     cycle++; if (cycle > 5) cycle = 0;    // cycle through six images.
  207.     return self;
  208. }
  209.  
  210.  
  211. @end
  212.